#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/err.h>
#include <linux/ioctl.h>
#include <linux/ktime.h>

#define LED_TOGGLE _IO('L', 0) /* 切换 LED 状态 */
#define LED_ON     _IO('L', 1) /* 打开 LED */
#define LED_OFF    _IO('L', 2) /* 关闭 LED */
#define IOCTL_SET_DATA _IOW('L', 3, struct ioctl_data) /* 设置数据 */
#define IOCTL_GET_DATA _IOR('L', 4, struct ioctl_data) /* 获取数据 */

struct ioctl_data {
    size_t size; /* 数据大小 */
    char data[256]; /* 示例数据缓存区 */
};

struct led_test_data {
    struct gpio_desc *led_gpio;
    struct ioctl_data buffer; /* 存储数据的缓冲区 */
};

static struct led_test_data *led_data;

/* LED 控制函数 */
static ssize_t led_test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    ktime_t start, end;
    s64 elapsed_ns;

    if (count > sizeof(led_data->buffer.data))
        return -EINVAL;

    start = ktime_get();
    if (copy_from_user(led_data->buffer.data, buf, count))
        return -EFAULT;

    led_data->buffer.size = count;
    end = ktime_get();

    elapsed_ns = ktime_to_ns(ktime_sub(end, start));
    pr_info("Write operation took %lld ns\n", elapsed_ns);

    pr_info("Data written: %.*s\n", (int)count, led_data->buffer.data);

    return count;
}

static ssize_t led_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    ktime_t start, end;
    s64 elapsed_ns;

    if (*ppos > 0 || count < led_data->buffer.size)
        return 0;

    start = ktime_get();
    if (copy_to_user(buf, led_data->buffer.data, led_data->buffer.size))
        return -EFAULT;

    end = ktime_get();

    elapsed_ns = ktime_to_ns(ktime_sub(end, start));
    pr_info("Read operation took %lld ns\n", elapsed_ns);

    *ppos = led_data->buffer.size;
    return led_data->buffer.size;
}

/* IOCTL 实现 */
static long led_test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    static int led_state = 0; /* 记录当前 LED 状态 */
    ktime_t start, end;
    s64 elapsed_ns;
    pr_info("ioctl cmd %d\n", cmd);
    start = ktime_get();
    switch (cmd) {
    case LED_TOGGLE:
        led_state = !led_state;
        gpiod_set_value(led_data->led_gpio, led_state);
        pr_info("LED toggled to %d\n", led_state);
        break;

    case LED_ON:
        gpiod_set_value(led_data->led_gpio, 1);
        pr_info("LED turned on\n");
        break;

    case LED_OFF:
        gpiod_set_value(led_data->led_gpio, 0);
        pr_info("LED turned off\n");
        break;

    case IOCTL_SET_DATA: {
        struct ioctl_data tmp;
        if (copy_from_user(&tmp, (void __user *)arg, sizeof(tmp)))
            return -EFAULT;
        memcpy(&led_data->buffer, &tmp, sizeof(tmp));
        pr_info("Data set via IOCTL: %.*s\n", (int)tmp.size, tmp.data);
        break;
    }

    case IOCTL_GET_DATA:
        if (copy_to_user((void __user *)arg, &led_data->buffer, sizeof(led_data->buffer)))
            return -EFAULT;
        pr_info("Data retrieved via IOCTL\n");
        break;

    default:
        return -EINVAL;
    }

    end = ktime_get();
    elapsed_ns = ktime_to_ns(ktime_sub(end, start));
    pr_info("IOCTL operation took %lld ns\n", elapsed_ns);

    return 0;
}

static const struct file_operations led_test_fops = {
    .owner = THIS_MODULE,
    .write = led_test_write,
    .read = led_test_read,
    .unlocked_ioctl = led_test_ioctl, /* 添加 IOCTL 支持 */
};

static struct miscdevice led_misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "led_test",
    .fops = &led_test_fops,
};

static int led_test_probe(struct platform_device *pdev)
{
    int ret;

    /* 分配设备数据结构 */
    led_data = devm_kzalloc(&pdev->dev, sizeof(*led_data), GFP_KERNEL);
    if (!led_data)
        return -ENOMEM;

    /* 获取设备树中的 GPIO 描述符 */
    led_data->led_gpio = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
    if (IS_ERR(led_data->led_gpio)) {
        dev_err(&pdev->dev, "Failed to get GPIO for LED\n");
        return PTR_ERR(led_data->led_gpio);
    }

    /* 设置默认状态 */
    gpiod_set_value(led_data->led_gpio, 0);

    /* 注册 misc 设备 */
    ret = misc_register(&led_misc_device);
    if (ret) {
        dev_err(&pdev->dev, "Failed to register misc device\n");
        return ret;
    }

    dev_info(&pdev->dev, "LED test driver probed successfully\n");
    return 0;
}

static int led_test_remove(struct platform_device *pdev)
{
    /* 注销 misc 设备 */
    misc_deregister(&led_misc_device);

    /* 确保 LED 关闭 */
    gpiod_set_value(led_data->led_gpio, 0);
    dev_info(&pdev->dev, "LED test driver removed\n");
    return 0;
}

static const struct of_device_id led_test_of_match[] = {
    { .compatible = "led0-test" },
    {},
};
MODULE_DEVICE_TABLE(of, led_test_of_match);

static struct platform_driver led_test_driver = {
    .probe = led_test_probe,
    .remove = led_test_remove,
    .driver = {
        .name = "led_test",
        .of_match_table = led_test_of_match,
    },
};

module_platform_driver(led_test_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("LED Test Driver using GPIO Subsystem with IOCTL and Timing");
